﻿#include "EditorSceneOutlineAbilityViewModel.h"

#include "Asset/EditorDefaultPackage.h"
#include "EditorSceneOutlineAbilityModel.h"

#include "Scene/SceneNode.h"
#include "Scene/Scene.h"

#include <stack>

#include "Widgets/EditableText/EditorEditableTextViewModel.h"

void REditorSceneOutlineAbilityViewModel::BindModel(TSharedObjectPtr<REditorAbilityModel> InModel)
{
    Super::BindModel(InModel);

    Model=InModel;

    Icon = REditorDefaultPackage::Get()->GetIcon(U"SceneNodeTree");
    HighlightIcon = REditorDefaultPackage::Get()->GetIcon(U"SceneNodeTree_Highlight");

    CollapsedIcon=REditorDefaultPackage::Get()->GetIcon(U"CodiconChevronRight");
    ExpandedIcon=REditorDefaultPackage::Get()->GetIcon(U"CodiconChevronDown");
  
    UpdateNodeTree();
    Model->OnSceneChanged.Bind([this]()
    {
        UpdateNodeTree();
        OnNodeTreeChanged.ExecuteIfBound();
    });

    NameEditViewModel=NewObject<REditorEditableTextViewModel>(this);
    NameEditViewModel->SetFontSize(FontSize);
    NameEditViewModel->OnEditingEnded.Add([this](REditorEditableTextViewModel::EReasonForEndEditing InReasonForEndEditing)
    {
        CString Name=NameEditViewModel->GetDisplayText();

        bool bValid= IsValidEditingName(Name).IsEmpty();
        if(bValid)
        {
            VisibleNodes[EditingListIndex]->SceneNode->SetNodeName(Name);
        }

        EditingListIndex=-1;
        NameEditViewModel->SetDisplayText(U""); //clear the text
    });

    NameEditViewModel->OnDisplayTextChanged.Add([this]()
    {
        //check if the name is valid
        CString Name= NameEditViewModel->GetDisplayText();
        EditingNameError=IsValidEditingName(Name);
    });

    NameEditViewModel->CanEndEditing.Bind([this](REditorEditableTextViewModel::EReasonForEndEditing InReasonForEndEditing)
    {
        if(InReasonForEndEditing== REditorEditableTextViewModel::EReasonForEndEditing::Confirm)
        {
            EditingNameError=IsValidEditingName(NameEditViewModel->GetDisplayText());
            return EditingNameError.IsEmpty();
        }
        return true;
    });
}

TSharedObjectPtr<RTexture2D> REditorSceneOutlineAbilityViewModel::GetIcon() const
{
    return Icon;
}

TSharedObjectPtr<RTexture2D> REditorSceneOutlineAbilityViewModel::GetHighlightIcon() const
{
    return HighlightIcon;
}

bool REditorSceneOutlineAbilityViewModel::HasNodes() const
{
    return RootNode!=nullptr;
}

int64_t REditorSceneOutlineAbilityViewModel::GetVisibleNodeNum() const
{
    return VisibleNodes.Num();
}

float REditorSceneOutlineAbilityViewModel::GetNodeDepth(int64_t Index) const
{
    return VisibleNodes[Index]->Depth*20;
}

TSharedObjectPtr<RTexture2D> REditorSceneOutlineAbilityViewModel::GetNodeIcon(int64_t Index) const
{
    if(VisibleNodes[Index]->bIsExpanded)
    {
        return ExpandedIcon;
    }
    else
    {
        if(HasSubNode(Index))
        {
            return CollapsedIcon;
        }
        else
        {
            return nullptr;
        
        }
    }
}

CString REditorSceneOutlineAbilityViewModel::GetNodeText(int64_t Index) const
{
    return VisibleNodes[Index]->SceneNode->GetNodeName();
}

void REditorSceneOutlineAbilityViewModel::OnLineClicked(int64_t Index)
{
    SetSelectedNodeIndex(Index);

    //检查是否有子节点
    if(!HasSubNode(Index))
    {
        return;
    }

    if(VisibleNodes[Index]->bIsExpanded)
    {
        //toggle expand state
        Model->ExpandedNodes.Remove(VisibleNodes[Index]->SceneNode);
    }
    else
    {
        //expand the node
        Model->ExpandedNodes.Add(VisibleNodes[Index]->SceneNode);
    }
    UpdateNodeTree();
    OnNodeTreeChanged.ExecuteIfBound();
    
}

bool REditorSceneOutlineAbilityViewModel::HasSubNode(int64_t Index) const
{
    if(VisibleNodes[Index]->SceneNode->IsA<RScene>())
    {
        TSharedObjectPtr<RScene> Scene=VisibleNodes[Index]->SceneNode;
        return Scene->GetNodeNum()>0;
    }
    else
    {
        TSharedObjectPtr<RSceneNode> SceneNode=VisibleNodes[Index]->SceneNode;
        return SceneNode->GetChildrenNum()>0;
    }
}

bool REditorSceneOutlineAbilityViewModel::HaveHoveredNode() const
{
    return HoveredNodeIndex!=-1;
}

int64_t REditorSceneOutlineAbilityViewModel::GetHoveredNodeIndex() const
{
    return HoveredNodeIndex;
}

void REditorSceneOutlineAbilityViewModel::SetHoveredNodeIndex(int64_t Index)
{
    HoveredNodeIndex=Index;
}

bool REditorSceneOutlineAbilityViewModel::HaveSelectedNode() const
{
    return SelectedNodeIndex!=-1;
}

int64_t REditorSceneOutlineAbilityViewModel::GetSelectedNodeIndex() const
{
    return SelectedNodeIndex;
}

void REditorSceneOutlineAbilityViewModel::SetSelectedNodeIndex(int64_t Index)
{
    SelectedNodeIndex=Index;

    if(SelectedNodeIndex!=-1)
    {
        auto Node=VisibleNodes[SelectedNodeIndex];
        Model->FocusNode(Node->SceneNode);
    }
    else
    {
        Model->FocusNode(nullptr);
    }
}

void REditorSceneOutlineAbilityViewModel::AddNewNode()
{
    if(!RootNode)
    {
        //no scene opened
        return;
    }

    TSharedObjectPtr<RSceneNode> NewNode;
    if(SelectedNodeIndex==-1 //没有选择任何节点
        ||SelectedNodeIndex==0  //选择的是场景
    )
    {
        NewNode=Model->Scene->NewNode<RSceneNode>();

        Model->ExpandedNodes.AddUnique(Model->Scene);
    }
    else
    {
        TSharedObjectPtr<RSceneNode> ParentNode=VisibleNodes[SelectedNodeIndex]->SceneNode;
        NewNode=Model->Scene->NewNode<RSceneNode>();
        ParentNode->AddSubNode(NewNode,false);

        Model->ExpandedNodes.AddUnique(ParentNode);
    }


    UpdateNodeTree();

    //找到新节点的位置
    for(int64_t LineIndex = 0; LineIndex < VisibleNodes.Num(); ++LineIndex)
    {
        if(VisibleNodes[LineIndex]->SceneNode==NewNode)
        {
            EditingListIndex=LineIndex;
            break;
        }
    }
    assert(EditingListIndex!=-1);


    OnNodeTreeChanged.ExecuteIfBound();

    NameEditViewModel->SetDisplayText(NewNode->GetNodeName());
    NameEditViewModel->StartEditing();
}

void REditorSceneOutlineAbilityViewModel::RenameFocusedNode()
{
    if(SelectedNodeIndex==-1)
    {
        return;
    }

    EditingListIndex=SelectedNodeIndex;
    NameEditViewModel->SetDisplayText(VisibleNodes[SelectedNodeIndex]->SceneNode->GetNodeName());
    NameEditViewModel->StartEditing();
}

void REditorSceneOutlineAbilityViewModel::DeleteFocusedNode()
{
    if(SelectedNodeIndex==-1  //没有选择任何节点
    || SelectedNodeIndex==0   //不能删除场景
    )
    {
        return;
    }
    
    if(SelectedNodeIndex==VisibleNodes.Num()-1)
    {
        Model->FocusNode(nullptr);
    }
    else
    {
        Model->FocusNode(VisibleNodes[SelectedNodeIndex+1]->SceneNode);
    }

    auto Index=Model->ExpandedNodes.Find(VisibleNodes[SelectedNodeIndex]
        ->SceneNode);
    if(Index.HasValue())
    {
        Model->ExpandedNodes.RemoveAt(Index.GetValue());
    }

    TSharedObjectPtr<RSceneNode> SceneNode=VisibleNodes[SelectedNodeIndex]->SceneNode;
    SceneNode->RemoveFromParent();

    UpdateNodeTree();
    OnNodeTreeChanged.ExecuteIfBound();
}

void REditorSceneOutlineAbilityViewModel::UpdateNodeTree()
{
    if(!Model->Scene)
    {
        RootNode=nullptr;
        VisibleNodes.Clear();
        SelectedNodeIndex=-1;
        HoveredNodeIndex=-1;
        return;
    }

    RootNode=std::make_shared<SOutlineNode>();
    RootNode->SceneNode=Model->Scene;
    RootNode->Depth=0;
    RootNode->VisibleIndex=0;

    VisibleNodes.Clear();

    std::stack<std::shared_ptr<SOutlineNode>> NodesToProcess;
    NodesToProcess.push(RootNode);

    while(!NodesToProcess.empty())
    {
        auto Node=NodesToProcess.top();
        NodesToProcess.pop();

        Node->bIsExpanded=Model->IsNodeExpanded(Node->SceneNode);

        VisibleNodes.Add(Node);
        Node->VisibleIndex=VisibleNodes.Num()-1;

        if(Node->bIsExpanded)
        {
            if(Node->SceneNode->IsA<RScene>())
            {
                TSharedObjectPtr<RScene> Scene=Node->SceneNode;
                for(int64_t i=0;i<Scene->GetNodeNum();++i)
                {
                    auto SceneNode=Scene->GetNode(i);
                    if(!SceneNode->IsRootNode())
                    {
                        continue;
                    }

                    auto NewNode=std::make_shared<SOutlineNode>();
                    NewNode->SceneNode=SceneNode;
                    NewNode->Depth=Node->Depth+1;
                    Node->Children.Add(NewNode);

                    NodesToProcess.push(NewNode);
                }
            }
            else
            {
                TSharedObjectPtr<RSceneNode> SceneNode=Node->SceneNode;
                for(int64_t i=0;i<SceneNode->GetChildrenNum();++i)
                {
                    auto Child=SceneNode->GetChild(i);
                    auto NewNode=std::make_shared<SOutlineNode>();
                    NewNode->SceneNode=Child;
                    NewNode->Depth=Node->Depth+1;
                    Node->Children.Add(NewNode);

                    NodesToProcess.push(NewNode);
                }
            }
        }
    }

    SelectedNodeIndex=-1;
    for(int64_t LineIndex = 0; LineIndex < VisibleNodes.Num(); ++LineIndex)
    {
        if(VisibleNodes[LineIndex]->SceneNode==Model->GetFocusedNode())
        {
            SelectedNodeIndex=LineIndex;
            break;
        }
    }

    if(HoveredNodeIndex>=VisibleNodes.Num())
    {
        HoveredNodeIndex=-1;
    }

}

CString REditorSceneOutlineAbilityViewModel::IsValidEditingName(const CString & InNodeName)
{
    if(InNodeName.IsEmpty())
    {
        return U"节点名不能为空";
    }

    return U"";
}
